home *** CD-ROM | disk | FTP | other *** search
- /* PlayTrackInfoThang.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #include "PlayTrackInfoThang.h"
- #include "Array.h"
- #include "Memory.h"
- #include "OscBankPlayer.h"
- #include "TrackObject.h"
- #include "IncrementalParameterUpdator.h"
- #include "Fractions.h"
- #include "FrameObject.h"
- #include "DeterminedNoteStructure.h"
- #include "NoteObject.h"
- #include "ErrorDaemon.h"
-
-
- typedef struct FrozenNoteConsCell
- {
- /* list link */
- struct FrozenNoteConsCell* Next;
-
- /* good information to be used for tied notes */
- FrozenNoteRec* FrozenNote;
-
- /* tie target for tied notes, NIL if there is no tie */
- struct NoteObjectRec* TieTarget;
-
- /* when this note should take over (in absolute envelope ticks) */
- long ContinuationTime;
- } FrozenNoteConsCell;
-
-
- typedef struct OscBankConsCell
- {
- /* link for listing */
- struct OscBankConsCell* Next;
- struct OscBankConsCell* Previous;
-
- /* the oscillator bank */
- OscStateBankRec* OscBank;
-
- /* still active flag */
- MyBoolean StillActive;
-
- /* tie target for tied notes, NIL if there is no tie */
- struct NoteObjectRec* TieTarget;
-
- /* this is the start time of the note, used for ordering the scanning gap list */
- long StartTime;
-
- /* this is an ordered list of frozen notes ready for tie continuation */
- /* this is in sorted ascending order of TieContinuationList->ContinuationTime */
- FrozenNoteConsCell* TieContinuationList;
- } OscBankConsCell;
-
-
- struct PlayTrackInfoRec
- {
- /* frame source */
- TrackObjectRec* TrackObject;
- /* total number of frames in the track object */
- long TotalNumberOfFrames;
- /* index into frame array. */
- long FrameArrayIndex;
- /* number of cycles until the next frame should be processed. this is in */
- /* units of duration update cycles. when this runs out, then another frame */
- /* should be processed. */
- long NextFrameCountDown;
-
- /* list of notes that have been scheduled but haven't been executed yet. */
- /* it contains objects of type OscBankConsCell. */
- OscBankConsCell* ScanningGapListHead; /* of OscBankConsCell's */
- OscBankConsCell* ScanningGapListTail; /* of OscBankConsCell's */
- /* this is the current duration update index for the scanning gap list */
- long ExecutionIndex;
- /* this is the index of the first element in the scanning gap list. when */
- /* the execution index hits or exceeds this value, the first osc bank is popped */
- /* from the scanning gap list and executed. it is only valid when */
- /* ScanningGapLength is greater than zero (i.e. ScanningGapList is non-empty) */
-
- /* this object keeps track of the current value of all parameters, updates them */
- /* as time passes, and evaluates commands passed in from here. */
- IncrParamUpdateRec* ParameterController;
-
- /* this is the template used for creating oscillator banks */
- OscBankTemplateRec* OscillatorBankTemplate;
-
- /* this is a list of all currently executing oscillator banks. there is one */
- /* entry for each note that is currently being played. */
- OscBankConsCell* ExecutingOscillatorBanks;
-
- /* playback control parameters */
- float EnvelopeUpdateRate;
- float OverallVolumeScaling;
- };
-
-
- static OscBankConsCell* OscBankConsCellFreeList = NIL;
- struct FrozenNoteConsCell* FrozenNoteConsCellFreeList = NIL;
-
-
- /* dispose of cached track data structures */
- void FlushPlayTrackInfo(void)
- {
- while (OscBankConsCellFreeList != NIL)
- {
- OscBankConsCell* Temp;
-
- Temp = OscBankConsCellFreeList;
- OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
- ReleasePtr((char*)Temp);
- }
-
- while (FrozenNoteConsCellFreeList != NIL)
- {
- FrozenNoteConsCell* Temp;
-
- Temp = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
- ReleasePtr((char*)Temp);
- }
- }
-
-
- /* create a new track play info thing and set a bunch of parameters. this also */
- /* builds the internal representations for instruments & oscillators for this track. */
- PlayTrackInfoRec* NewPlayTrackInfo(struct TrackObjectRec* TheTrack,
- struct InstrumentRec* InstrumentSpecification,
- MyBoolean StereoFlag, LargeBCDType OverallVolumeScalingReciprocal,
- long SamplingRate, float EnvelopeRate, MyBoolean TimeInterp,
- MyBoolean WaveInterp, struct TempoControlRec* TempoControl,
- long ScanningGapWidthInEnvelopeTicks, ErrorDaemonRec* ErrorDaemon)
- {
- PlayTrackInfoRec* TrackInfo;
-
- CheckPtrExistence(TheTrack);
- CheckPtrExistence(InstrumentSpecification);
- CheckPtrExistence(TempoControl);
- CheckPtrExistence(ErrorDaemon);
-
- TrackInfo = (PlayTrackInfoRec*)AllocPtrCanFail(sizeof(PlayTrackInfoRec),
- "PlayTrackInfoRec");
- if (TrackInfo == NIL)
- {
- FailurePoint1:
- return NIL;
- }
-
- /* frame source */
- TrackInfo->TrackObject = TheTrack;
-
- /* total number of frames in the track object */
- TrackInfo->TotalNumberOfFrames = TrackObjectGetNumFrames(TheTrack);
-
- /* index into frame array. */
- TrackInfo->FrameArrayIndex = 0;
-
- /* number of cycles until the next frame should be processed. this is in */
- /* units of duration update cycles. when this runs out, then another frame */
- /* should be processed. */
- TrackInfo->NextFrameCountDown = 0;
-
- /* list of notes that have been scheduled but haven't been executed yet. */
- /* it contains objects of type OscStateBankRec. */
- TrackInfo->ScanningGapListHead = NIL;
- TrackInfo->ScanningGapListTail = NIL;
-
- /* this is the current envelope update index for removing things from the */
- /* scanning gap list (i.e. the back edge of the scanning gap) */
- TrackInfo->ExecutionIndex = - ScanningGapWidthInEnvelopeTicks;
-
- /* this object keeps track of the current value of all parameters, updates them */
- /* as time passes, and evaluates commands passed in from here. */
- TrackInfo->ParameterController = NewInitializedParamUpdator(TheTrack,TempoControl);
- if (TrackInfo->ParameterController == NIL)
- {
- FailurePoint2:
- ReleasePtr((char*)TrackInfo);
- goto FailurePoint1;
- }
-
- /* this is the template used for creating oscillator banks */
- TrackInfo->OscillatorBankTemplate = NewOscBankTemplate(InstrumentSpecification,
- StereoFlag,OverallVolumeScalingReciprocal,SamplingRate,EnvelopeRate,
- TimeInterp,WaveInterp,TrackInfo->ParameterController,ErrorDaemon);
- if (TrackInfo->OscillatorBankTemplate == NIL)
- {
- FailurePoint3:
- DisposeParamUpdator(TrackInfo->ParameterController);
- goto FailurePoint2;
- }
-
- /* this is a list of all currently executing oscillator banks. there is one */
- /* entry for each note that is currently being played. */
- TrackInfo->ExecutingOscillatorBanks = NIL;
-
- /* playback control parameters */
- TrackInfo->EnvelopeUpdateRate = EnvelopeRate;
- TrackInfo->OverallVolumeScaling = (float)1
- / LargeBCD2Double(OverallVolumeScalingReciprocal);
-
- return TrackInfo;
- }
-
-
- static void DisposeOscBankList(OscBankConsCell* List)
- {
- while (List != NIL)
- {
- OscBankConsCell* Temp;
-
- /* delink */
- Temp = List;
- List = List->Next;
- /* dispose members */
- DisposeOscStateBank(Temp->OscBank);
- while (Temp->TieContinuationList != NIL)
- {
- FrozenNoteConsCell* ColdTemp;
-
- ColdTemp = Temp->TieContinuationList;
- Temp->TieContinuationList = Temp->TieContinuationList->Next;
- DisposeFrozenNote(ColdTemp->FrozenNote);
- ColdTemp->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = ColdTemp;
- }
- /* stick on free list */
- Temp->Next = OscBankConsCellFreeList;
- EXECUTE(Temp->Previous = (OscBankConsCell*)0x81818181;)
- OscBankConsCellFreeList = Temp;
- }
- }
-
-
- /* dump a track play info thing and all the stuff in it */
- void DisposePlayTrackInfo(PlayTrackInfoRec* TrackInfo)
- {
- CheckPtrExistence(TrackInfo);
- DisposeOscBankList(TrackInfo->ScanningGapListHead);
- DisposeOscBankList(TrackInfo->ExecutingOscillatorBanks);
- DisposeOscBankTemplate(TrackInfo->OscillatorBankTemplate);
- DisposeParamUpdator(TrackInfo->ParameterController);
- ReleasePtr((char*)TrackInfo);
- }
-
-
- /* cue track forward to the specified point. returns False if it fails */
- MyBoolean CuePlayTrackInfoToPoint(PlayTrackInfoRec* TrackInfo,
- struct FractionRec* StartTime)
- {
- long AdvancementCounter;
-
- CheckPtrExistence(TrackInfo);
-
- /* convert whole-note fraction into duration ticks */
- ERROR(StartTime->Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
- "CuePlayTrackInfoToPoint: start time denominator has bad value"));
- AdvancementCounter = StartTime->Integer * StartTime->Denominator
- + StartTime->Fraction;
-
- /* search for the proper point to start playing */
- while ((AdvancementCounter > 0)
- && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
- {
- FrameObjectRec* Frame; /* I've been framed! */
- FractionRec Duration;
- long TicksInFrame;
-
- /* get the frame */
- Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
- CheckPtrExistence(Frame);
- /* how long is it? */
- DurationOfFrame(Frame,&Duration);
- ERROR(Duration.Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
- "CuePlayTrackInfoToPoint: start time denominator has odd value"));
- TicksInFrame = Duration.Integer * Duration.Denominator + Duration.Fraction;
- /* decrement our lead-in counter */
- AdvancementCounter -= TicksInFrame;
- /* advance our pointer */
- TrackInfo->FrameArrayIndex += 1;
- }
-
- /* AdvancementCounter is either 0 or negative. if it's negative, then we */
- /* need to note that we must delay some before getting the next note */
- TrackInfo->NextFrameCountDown = - AdvancementCounter;
-
- return True;
- }
-
-
- /* check to see if the track has finished and can be dropped. */
- MyBoolean PlayTrackIsItStillActive(PlayTrackInfoRec* TrackInfo)
- {
- OscBankConsCell* OscBankScan;
-
- CheckPtrExistence(TrackInfo);
-
- /* in order to still be active, the following conditions must be satisfied: */
- /* - there are still active oscillators. */
- /* - there are still oscillators in the scanning gap list */
- /* - there are still notes which haven't been scanned yet */
-
- if (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames)
- {
- return True;
- }
-
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- return True;
- }
-
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- while (OscBankScan != NIL)
- {
- if (OscBankScan->StillActive)
- {
- return True;
- }
- OscBankScan = OscBankScan->Next;
- }
-
- return False;
- }
-
-
- /* auxilliary routine which searches a list for a tie source and installs the note */
- /* in the list if necessary. True is returned if it is installed. */
- static MyBoolean SearchForTieSource(OscBankConsCell* OscBankScan,
- struct NoteObjectRec* Note, long ScanningGapFrontInEnvelopeTicks,
- PlayTrackInfoRec* TrackInfo, float EnvelopeTicksPerDurationTick)
- {
- while (OscBankScan != NIL)
- {
- FrozenNoteConsCell* PlaceToPut;
- long StartAdjust;
-
- PlaceToPut = NIL;
- if (OscBankScan->TieTarget == Note)
- {
- /* found it */
- CreateTieContinuationPoint:
- if (FrozenNoteConsCellFreeList != NIL)
- {
- PlaceToPut = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
- }
- else
- {
- PlaceToPut = (FrozenNoteConsCell*)AllocPtrCanFail(
- sizeof(FrozenNoteConsCell),"FrozenNoteConsCell");
- if (PlaceToPut == NIL)
- {
- return False;
- }
- }
- }
- else
- {
- FrozenNoteConsCell* TargScan;
- FrozenNoteConsCell* TargLag;
-
- /* search tie target things */
- TargScan = OscBankScan->TieContinuationList;
- TargLag = NIL;
- while (TargScan != NIL)
- {
- if (TargScan->TieTarget == Note)
- {
- /* found one */
- goto CreateTieContinuationPoint;
- }
- TargLag = TargScan;
- TargScan = TargScan->Next;
- }
- }
- if (PlaceToPut != NIL)
- {
- FrozenNoteConsCell* InsertScan;
- FrozenNoteConsCell* InsertLag;
-
- /* fill in the fields */
- PlaceToPut->FrozenNote = FixNoteParameters(
- TrackInfo->ParameterController,Note,&StartAdjust,
- TrackInfo->OverallVolumeScaling,EnvelopeTicksPerDurationTick);
- if (PlaceToPut->FrozenNote == NIL)
- {
- PlaceToPut->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = PlaceToPut;
- return False;
- }
- PlaceToPut->ContinuationTime = StartAdjust + ScanningGapFrontInEnvelopeTicks;
- PlaceToPut->TieTarget = GetNoteTieTarget(Note);
- /* insert it into the proper place */
- InsertScan = OscBankScan->TieContinuationList;
- InsertLag = NIL;
- while ((InsertScan != NIL) && (InsertScan->ContinuationTime
- <= PlaceToPut->ContinuationTime))
- {
- InsertLag = InsertScan;
- InsertScan = InsertScan->Next;
- }
- PlaceToPut->Next = InsertScan;
- if (InsertLag == NIL)
- {
- OscBankScan->TieContinuationList = PlaceToPut;
- }
- else
- {
- InsertLag->Next = PlaceToPut;
- }
- /* we found it! */
- return True;
- }
- OscBankScan = OscBankScan->Next;
- }
- return False;
- }
-
-
- /* perform one envelope clock cycle update. if UpdateEnvelopes is true, then */
- /* wave data should be generated and envelopes should be updated, otherwise only */
- /* note scheduling should be performed. */
- MyBoolean PlayTrackUpdate(PlayTrackInfoRec* TrackInfo,
- MyBoolean UpdateEnvelopes, long NumDurationTicks,
- long NumFrames, largefixedsigned* OutputData,
- float EnvelopeTicksPerDurationTick,
- long ScanningGapFrontInEnvelopeTicks)
- {
- OscBankConsCell* OscBankScan;
- OscBankConsCell* OscBankLag;
-
- CheckPtrExistence(TrackInfo);
-
- /* schedule any notes out of the track list into the scanning gap */
- while ((TrackInfo->NextFrameCountDown <= 0)
- && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
- {
- FrameObjectRec* Frame;
-
- /* schedule a frame */
- Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
- CheckPtrExistence(Frame);
- TrackInfo->FrameArrayIndex += 1;
- if (IsThisACommandFrame(Frame))
- {
- /* it's a command */
- ExecuteParamCommandFrame(TrackInfo->ParameterController,Frame);
- }
- else
- {
- long FrameLimit;
- long FrameScan;
- FractionRec FrameDuration;
-
- /* increment the frame counter */
- DurationOfFrame(Frame,&FrameDuration);
- ERROR(DURATIONUPDATECLOCKRESOLUTION != FrameDuration.Denominator,
- PRERR(AllowResume,"PlayTrackUpdate: strange denominator in frame duration"));
- TrackInfo->NextFrameCountDown += FrameDuration.Denominator
- * FrameDuration.Integer + FrameDuration.Fraction;
-
- /* it's a real note */
- FrameLimit = NumNotesInFrame(Frame);
- for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
- {
- struct NoteObjectRec* Note;
- OscBankConsCell* NewOscBank;
- OscBankConsCell* LinkingScan;
-
- Note = GetNoteFromFrame(Frame,FrameScan);
- CheckPtrExistence(Note);
- if (GetNoteIsItARest(Note))
- {
- /* just ignore rests */
- goto EndFrameScanPoint; /* this should really be a conditional */
- }
-
- /* first, scan the oscillator list to see if this is a note that */
- /* someone wants to tie to. if it is, then build a frozen note */
- /* structure and add it to the object. otherwise, build an */
- /* oscillator bank and add it to the scanning gap. */
-
- /* search all oscillator banks to see if it's a tie target */
- if (SearchForTieSource(TrackInfo->ExecutingOscillatorBanks,Note,
- ScanningGapFrontInEnvelopeTicks,TrackInfo,
- EnvelopeTicksPerDurationTick))
- {
- goto EndFrameScanPoint;
- }
- if (SearchForTieSource(TrackInfo->ScanningGapListHead,Note,
- ScanningGapFrontInEnvelopeTicks,TrackInfo,
- EnvelopeTicksPerDurationTick))
- {
- goto EndFrameScanPoint;
- }
-
- /* if we got here, then it's not a tie target */
- if (OscBankConsCellFreeList != NIL)
- {
- NewOscBank = OscBankConsCellFreeList;
- OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
- }
- else
- {
- NewOscBank = (OscBankConsCell*)AllocPtrCanFail(
- sizeof(OscBankConsCell),"OscBankConsCell");
- if (NewOscBank == NIL)
- {
- return False;
- }
- }
- NewOscBank->OscBank = NewOscBankState(TrackInfo->OscillatorBankTemplate,
- &(NewOscBank->StartTime),Note,EnvelopeTicksPerDurationTick);
- if (NewOscBank->OscBank == NIL)
- {
- NewOscBank->Next = OscBankConsCellFreeList;
- OscBankConsCellFreeList = NewOscBank;
- return False;
- }
- NewOscBank->StartTime += ScanningGapFrontInEnvelopeTicks; /* fix up start time */
- NewOscBank->StillActive = True;
- NewOscBank->TieTarget = GetOscStateTieTarget(NewOscBank->OscBank);
- NewOscBank->TieContinuationList = NIL;
- /* link it in */
- LinkingScan = TrackInfo->ScanningGapListTail;
- while ((LinkingScan != NIL)
- && (LinkingScan->StartTime > NewOscBank->StartTime))
- {
- LinkingScan = LinkingScan->Previous;
- }
- if (LinkingScan == NIL)
- {
- NewOscBank->Previous = NIL;
- NewOscBank->Next = TrackInfo->ScanningGapListHead;
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- TrackInfo->ScanningGapListHead->Previous = NewOscBank;
- }
- TrackInfo->ScanningGapListHead = NewOscBank;
- if (TrackInfo->ScanningGapListTail == NIL)
- {
- /* this happens if there were no nodes at all */
- TrackInfo->ScanningGapListTail = NewOscBank;
- }
- }
- else
- {
- /* insert after Scan */
- NewOscBank->Previous = LinkingScan;
- NewOscBank->Next = LinkingScan->Next;
- LinkingScan->Next = NewOscBank;
- if (LinkingScan == TrackInfo->ScanningGapListTail)
- {
- /* this happens if Scan was the last element; */
- /* NewNode becomes last element */
- TrackInfo->ScanningGapListTail = NewOscBank;
- }
- }
-
- EndFrameScanPoint:
- ;
- }
- }
- }
-
- /* do timing update */
- TrackInfo->NextFrameCountDown -= NumDurationTicks;
-
- /* update global parameters */
- ExecuteParamUpdate(TrackInfo->ParameterController,NumDurationTicks);
-
- /* generate waveforms, update envelope generators & notes, etc. */
- if (UpdateEnvelopes)
- {
- /* see if any ties have to be tripped */
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- while (OscBankScan != NIL)
- {
- while ((OscBankScan->TieContinuationList != NIL)
- && (OscBankScan->TieContinuationList->ContinuationTime
- <= TrackInfo->ExecutionIndex))
- {
- FrozenNoteConsCell* Temp;
-
- /* yow, let's do this one! */
- /* delink */
- Temp = OscBankScan->TieContinuationList;
- OscBankScan->TieContinuationList
- = OscBankScan->TieContinuationList->Next;
- /* execute */
- if (!ResetOscBankState(OscBankScan->OscBank,Temp->FrozenNote,
- EnvelopeTicksPerDurationTick))
- {
- return False; /* oh, no! */
- }
- OscBankScan->TieTarget = Temp->TieTarget;
- /* clean up */
- DisposeFrozenNote(Temp->FrozenNote);
- Temp->Next = FrozenNoteConsCellFreeList;
- FrozenNoteConsCellFreeList = Temp;
- }
- OscBankScan = OscBankScan->Next;
- }
-
- /* schedule a note from the scanning gap */
- while ((TrackInfo->ScanningGapListHead != NIL)
- && (TrackInfo->ScanningGapListHead->StartTime <= TrackInfo->ExecutionIndex))
- {
- OscBankConsCell* NewConsCell;
-
- /* yup, schedule the oscillator */
- NewConsCell = TrackInfo->ScanningGapListHead;
- TrackInfo->ScanningGapListHead = TrackInfo->ScanningGapListHead->Next;
- if (TrackInfo->ScanningGapListHead != NIL)
- {
- TrackInfo->ScanningGapListHead->Previous = NIL;
- }
- else
- {
- TrackInfo->ScanningGapListTail = NIL;
- }
- /* link it in */
- NewConsCell->Next = TrackInfo->ExecutingOscillatorBanks;
- TrackInfo->ExecutingOscillatorBanks = NewConsCell;
- EXECUTE(NewConsCell->Previous = (OscBankConsCell*)0x81818181;)
- }
-
- /* increment our scanning gap back edge clock */
- TrackInfo->ExecutionIndex += 1;
-
- /* wave generator and envelope update loop */
- OscBankScan = TrackInfo->ExecutingOscillatorBanks;
- OscBankLag = NIL;
- while (OscBankScan != NIL)
- {
- OscBankScan->StillActive = !UpdateOscStateBank(OscBankScan->OscBank,
- NumFrames,OutputData);
- if (!OscBankScan->StillActive && (OscBankScan->TieContinuationList == NIL))
- {
- OscBankConsCell* Temp;
-
- /* not tied to anybody, so kill it */
- DisposeOscStateBank(OscBankScan->OscBank);
- if (OscBankLag == NIL)
- {
- TrackInfo->ExecutingOscillatorBanks = OscBankScan->Next;
- }
- else
- {
- OscBankLag->Next = OscBankScan->Next;
- }
- Temp = OscBankScan;
- OscBankScan = OscBankScan->Next;
- Temp->Next = OscBankConsCellFreeList;
- OscBankConsCellFreeList = Temp;
- }
- else
- {
- OscBankLag = OscBankScan;
- OscBankScan = OscBankScan->Next;
- }
- }
- }
-
- return True;
- }
-